// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/quota/mock_quota_manager.h"

#include <memory>
#include <set>

#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/threading/thread_task_runner_handle.h"
#include "content/public/test/mock_special_storage_policy.h"
#include "content/public/test/mock_storage_client.h"
#include "testing/gtest/include/gtest/gtest.h"

using storage::kQuotaStatusOk;
using storage::kStorageTypePersistent;
using storage::kStorageTypeTemporary;

namespace content {

const char kTestOrigin1[] = "http://host1:1/";
const char kTestOrigin2[] = "http://host2:1/";
const char kTestOrigin3[] = "http://host3:1/";

const GURL kOrigin1(kTestOrigin1);
const GURL kOrigin2(kTestOrigin2);
const GURL kOrigin3(kTestOrigin3);

const StorageType kTemporary = kStorageTypeTemporary;
const StorageType kPersistent = kStorageTypePersistent;

const QuotaClient::ID kClientFile = QuotaClient::kFileSystem;
const QuotaClient::ID kClientDB = QuotaClient::kIndexedDatabase;

class MockQuotaManagerTest : public testing::Test {
public:
    MockQuotaManagerTest()
        : deletion_callback_count_(0)
        , weak_factory_(this)
    {
    }

    void SetUp() override
    {
        ASSERT_TRUE(data_dir_.CreateUniqueTempDir());
        policy_ = new MockSpecialStoragePolicy;
        manager_ = new MockQuotaManager(
            false /* is_incognito */, data_dir_.GetPath(),
            base::ThreadTaskRunnerHandle::Get().get(),
            base::ThreadTaskRunnerHandle::Get().get(), policy_.get());
    }

    void TearDown() override
    {
        // Make sure the quota manager cleans up correctly.
        manager_ = NULL;
        base::RunLoop().RunUntilIdle();
    }

    void GetModifiedOrigins(StorageType type, base::Time since)
    {
        manager_->GetOriginsModifiedSince(
            type, since,
            base::Bind(&MockQuotaManagerTest::GotModifiedOrigins,
                weak_factory_.GetWeakPtr()));
    }

    void GotModifiedOrigins(const std::set<GURL>& origins, StorageType type)
    {
        origins_ = origins;
        type_ = type;
    }

    void DeleteOriginData(const GURL& origin, StorageType type,
        int quota_client_mask)
    {
        manager_->DeleteOriginData(
            origin, type, quota_client_mask,
            base::Bind(&MockQuotaManagerTest::DeletedOriginData,
                weak_factory_.GetWeakPtr()));
    }

    void DeletedOriginData(QuotaStatusCode status)
    {
        ++deletion_callback_count_;
        EXPECT_EQ(kQuotaStatusOk, status);
    }

    int deletion_callback_count() const
    {
        return deletion_callback_count_;
    }

    MockQuotaManager* manager() const
    {
        return manager_.get();
    }

    const std::set<GURL>& origins() const
    {
        return origins_;
    }

    const StorageType& type() const
    {
        return type_;
    }

private:
    base::MessageLoop message_loop_;
    base::ScopedTempDir data_dir_;
    scoped_refptr<MockQuotaManager> manager_;
    scoped_refptr<MockSpecialStoragePolicy> policy_;

    int deletion_callback_count_;

    std::set<GURL> origins_;
    StorageType type_;

    base::WeakPtrFactory<MockQuotaManagerTest> weak_factory_;

    DISALLOW_COPY_AND_ASSIGN(MockQuotaManagerTest);
};

TEST_F(MockQuotaManagerTest, BasicOriginManipulation)
{
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));

    manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));

    manager()->AddOrigin(kOrigin1, kPersistent, kClientFile, base::Time::Now());
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));

    manager()->AddOrigin(kOrigin2, kTemporary, kClientFile | kClientDB,
        base::Time::Now());
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kTemporary, kClientDB));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin1, kPersistent, kClientDB));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kPersistent, kClientDB));
}

TEST_F(MockQuotaManagerTest, OriginDeletion)
{
    manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, base::Time::Now());
    manager()->AddOrigin(kOrigin2, kTemporary, kClientFile | kClientDB,
        base::Time::Now());
    manager()->AddOrigin(kOrigin3, kTemporary, kClientFile | kClientDB,
        base::Time::Now());

    DeleteOriginData(kOrigin2, kTemporary, kClientFile);
    base::RunLoop().RunUntilIdle();

    EXPECT_EQ(1, deletion_callback_count());
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin3, kTemporary, kClientFile));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin3, kTemporary, kClientDB));

    DeleteOriginData(kOrigin3, kTemporary, kClientFile | kClientDB);
    base::RunLoop().RunUntilIdle();

    EXPECT_EQ(2, deletion_callback_count());
    EXPECT_TRUE(manager()->OriginHasData(kOrigin1, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin2, kTemporary, kClientFile));
    EXPECT_TRUE(manager()->OriginHasData(kOrigin2, kTemporary, kClientDB));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin3, kTemporary, kClientFile));
    EXPECT_FALSE(manager()->OriginHasData(kOrigin3, kTemporary, kClientDB));
}

TEST_F(MockQuotaManagerTest, ModifiedOrigins)
{
    base::Time now = base::Time::Now();
    base::Time then = base::Time();
    base::TimeDelta an_hour = base::TimeDelta::FromMilliseconds(3600000);
    base::TimeDelta a_minute = base::TimeDelta::FromMilliseconds(60000);

    GetModifiedOrigins(kTemporary, then);
    base::RunLoop().RunUntilIdle();
    EXPECT_TRUE(origins().empty());

    manager()->AddOrigin(kOrigin1, kTemporary, kClientFile, now - an_hour);

    GetModifiedOrigins(kTemporary, then);
    base::RunLoop().RunUntilIdle();

    EXPECT_EQ(kTemporary, type());
    EXPECT_EQ(1UL, origins().size());
    EXPECT_EQ(1UL, origins().count(kOrigin1));
    EXPECT_EQ(0UL, origins().count(kOrigin2));

    manager()->AddOrigin(kOrigin2, kTemporary, kClientFile, now);

    GetModifiedOrigins(kTemporary, then);
    base::RunLoop().RunUntilIdle();

    EXPECT_EQ(kTemporary, type());
    EXPECT_EQ(2UL, origins().size());
    EXPECT_EQ(1UL, origins().count(kOrigin1));
    EXPECT_EQ(1UL, origins().count(kOrigin2));

    GetModifiedOrigins(kTemporary, now - a_minute);
    base::RunLoop().RunUntilIdle();

    EXPECT_EQ(kTemporary, type());
    EXPECT_EQ(1UL, origins().size());
    EXPECT_EQ(0UL, origins().count(kOrigin1));
    EXPECT_EQ(1UL, origins().count(kOrigin2));
}
} // namespace content
